﻿// To start SampleApp.exe - Command line arguments
// for SERVER: /server localhost:8000
// for CLIENT: /client localhost:8001 localhost:8000

#define _RECONNECTION_TEST

using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Text;
using System.IO;
using System.Net;
using System.Threading;
using System.Configuration;
using System.Globalization;
using IL.TcpCommunicationLib;

namespace SubscriberSimulator
{
    class Program
    {
        #region Consts

        const string DEFAULT_QS_URL = "localhost";
        const int TIMER_PERIOD = 60 * 1000; // ms
       
        #endregion // Consts

        #region Enum

        enum Role
        { 
            None,
            Server,
            Client
        }

        #endregion // Enum

        #region Var

#if _RECONNECTION_TEST
        static Timer serverTimer = null;
#endif 

        static bool isListening = false;

        static int localPort = 0;
        static TcpChannelEventHandler<TcpChannelReceivedEventArgs> onReceived;
        static TcpChannelEventHandler<EventArgs> onInitConnectionToServer;
        static TcpChannelEventHandler<TcpChannelNotifyEventArgs> onServerNotifies;

        static int count = 0;

        #endregion // Var

        static void Main(string[] args)
        {
            Console.WriteLine("SampleApp has been started.");

            // Arguments processing
            if (args == null || args.Length < 2)
            {
                Console.WriteLine("ERROR in SampleApp: wrong arguments number.");
                WaitForReadKeyToExit();
                return;
            }

            Role role = Role.None;
            switch (args[0].ToLower())
            {
                case "/server": role = Role.Server; break;
                case "/client": role = Role.Client; break;
                default: 
                    Console.WriteLine("ERROR in SampleApp: wrong leading argument: can not decide whether server or client.");
                    WaitForReadKeyToExit();
                    return;
            }
            
            string[] ssLocal = args[1].Split(new char[] {':'});
            string localHost = ssLocal[0].ToLower();
            if (!int.TryParse(ssLocal[1], out localPort))
            {
                Console.WriteLine("ERROR in SampleApp: wrong local port argument.");
                WaitForReadKeyToExit();
                return;
            }

            string remoteHost = null;
            int remotePort = 0;
            if (role == Role.Client)
            {
                string[] ssRemote = args[2].Split(new char[] { ':' });
                remoteHost = ssRemote[0].ToLower();
                if (!int.TryParse(ssRemote[1], out remotePort))
                {
                    Console.WriteLine("ERROR in SampleApp: wrong remote port argument.");
                    WaitForReadKeyToExit();
                    return;
                }
            }

            // Set local host for all channels (both server and client) in the machine
            TcpChannel.LocalHost = localHost;

            onReceived = new TcpChannelEventHandler<TcpChannelReceivedEventArgs>((tcpChannelSender, e) =>
                {
                    if (e.AreBytesAvailable)
                    {
                        // Some processing of received bytes

                        Console.WriteLine("{0} {1}:{2} (Remote End Point {3}) received \"{4}\" at {5}", 
                                role == Role.Server ? "SERVER" : "CLIENT", 
                                TcpChannel.LocalIP, localPort, tcpChannelSender.RemoteEP, e.AsciiStringReceived, DateTime.Now);

                        if (tcpChannelSender != null)
                            tcpChannelSender.Send(string.Format("SenderID={0} ***{1}***", tcpChannelSender.Id, count++));

                        Thread.Sleep(50); // To reduce load - just for demo
                    }
                    
#if _RECONNECTION_TEST
                    if (role == Role.Server && serverTimer == null)
                        serverTimer = new Timer(new TimerCallback(OnTimerCallback), null, TIMER_PERIOD, TIMER_PERIOD);
#endif
                });

            switch (role)
            {
                case Role.Server:
                    onInitConnectionToServer = new TcpChannelEventHandler<EventArgs>((tcpServerSender, e) => 
                        {
                            SetEventHandlers(tcpServerSender);
                            tcpServerSender.Send(string.Format("{0}", tcpServerSender.Id));
                        });
                    onServerNotifies = new TcpChannelEventHandler<TcpChannelNotifyEventArgs>((tcpServerSender, e) => 
                        {
                            // Some notification processing, e.g. logging
                        });
                    TcpServer.StartAcceptSubscribersOnPort(localPort, onReceived, onInitConnectionToServer, onServerNotifies);
                    isListening = true;
                    break;

                case Role.Client:
                    TcpClient tcpClient = new TcpClient(onReceived,  
                                                        localPort.ToString(),   // id
                                                        15,                     // receiveTimeoutInSec
                                                         9,                     // reconnectionAttempts
                                                        10);                    // delayBetweenReconnectionAttemptsInSec   
                    SetEventHandlers(tcpClient);                                             
                    tcpClient.Connect(remoteHost, remotePort);
                    break;
            }
                                       
            WaitForReadKeyToExit();

#if _RECONNECTION_TEST
            if (serverTimer != null)
                serverTimer.Dispose();
#endif

            TcpChannel.CloseAllChannels();

            if (role == Role.Server)
                TcpServer.StopAcceptSubscribersOnPort(localPort);
        }

        #region Event Handlers

        static void SetEventHandlers(TcpChannel tcpChannel)
        {
            if (tcpChannel == null)
                return;

            tcpChannel.onSocketNullOrNotConnected += ((tcpChannelSender, e) => 
                { 
                    // Event handler    
                });

            // Other event handles assignment for TcpChannel

            tcpChannel.onProcessingThreadException += ((tcpChannelSender, e) => 
                {
                    OutputNotification("onProcessingThreadException", tcpChannelSender, e);
                });

            TcpClient tcpClient = tcpChannel as TcpClient;
            if (tcpClient != null)
            {
                tcpClient.onConnectionAttempt += ((tcpClientSender, e) =>
                {
                    count = 0;
                    OutputNotification("onConnectionAttempt", tcpClientSender, e);
                });

                tcpClient.onSocketConnectionFailed += ((tcpClientSender, e) =>
                {
                    //OutputNotification("onSocketConnectionFailed", tcpClientSender, e);
                });

                // Other event handles assignment for TcpClient
            }
        }

        #endregion // Event Handlers

#if _RECONNECTION_TEST

        #region Timer Callback

        static void OnTimerCallback(object state)
        {
            if (isListening)
            {
                TcpChannel.CloseAllChannels();
                TcpServer.StopAcceptSubscribersOnPort(localPort);
                isListening = false;
                count = 0;
            }
            else
            {
                TcpServer.StartAcceptSubscribersOnPort(localPort, onReceived, onInitConnectionToServer, onServerNotifies);
                isListening = true;
            }
        }

        #endregion // Timer Callback

#endif

        #region Helpres

        static void OutputNotification(string notifName, TcpChannel tcpChannelSender, TcpChannelNotifyEventArgs e)
        {
            Console.WriteLine("Id=\"{0}\" {1}: {2}, {3}", tcpChannelSender.Id, notifName, e.Data, e.Ex);   
        }

        static void WaitForReadKeyToExit()
        {
            Console.WriteLine("Press any key to exit... ");
            Console.ReadKey();
        }

        #endregion // Helpres
    }
}
